﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using ChatInterfaces;
using CHATCENTRAL.MAPPERS;
using CHATCENTRAL.Interfaces.Entities;
using System.Threading;
using Interfaces;

namespace Chat
{
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Single, InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = true)]
    public class ChatServer : IServerOperations
    {
        public Dictionary<string,IClientReceiverCallback> cli;
        private IChatCentralViewer View;

        public ChatServer(IChatCentralViewer v) 
        {
            this.View = v;
            cli = new Dictionary<string,IClientReceiverCallback>();
        }

        public IEnumerable<ClientToPass> Login(string user, string pwd)
        {
            IClient client = ClientMapper.GetInstance().GetUser(user, pwd);
            client.IsOnline = true;
            client.sessionId = OperationContext.Current.SessionId;

            View.logUsers(ClientMapper.GetInstance().GetAllUsers());

            ClientToPass Pessoa = new ClientToPass();
            Pessoa.username = client.username;
            Pessoa.BaseAddress = client.baseAddress;
            Pessoa.IsOnline = client.IsOnline;
            Pessoa.sessionID = client.sessionId;
            Pessoa.lang = client.Lang;

            IClient[] clients = Tema_User_Mapper.GetInstance().getUsersByTemas(Tema_User_Mapper.GetInstance().
                                           GetTemasForUser(client.username), client.username).ToArray();

            List<ClientToPass> client2Pass = new List<ClientToPass>();

            foreach (IClient c in clients)
            {
                ClientToPass c2p = new ClientToPass();
                c2p.username = c.username;
                c2p.BaseAddress = c.baseAddress;
                c2p.IsOnline = c.IsOnline;
                c2p.sessionID = c.sessionId;
                c2p.lang = c.Lang;
                client2Pass.Add(c2p);
            }

            cli.Add(client.username, OperationContext.Current.GetCallbackChannel<IClientReceiverCallback>());

            NotifyUsersLogin(Pessoa, clients);

            return client2Pass;
        }

        public void Logout(string user)
        {
            IClient client = ClientMapper.GetInstance().GetUser(user);
            client.IsOnline = false;
            client.sessionId = null;
            //OperationContext.Current.Channel.Close();
            cli.Remove(user);

            View.logUsers(ClientMapper.GetInstance().GetAllUsers());

            ClientToPass Pessoa = new ClientToPass();
            Pessoa.username = client.username;
            Pessoa.IsOnline = client.IsOnline;
            Pessoa.lang = client.Lang;
            NotifyUsersLogOut(Pessoa);
        }

        private void NotifyUsersLogin(ClientToPass c2p, IClient[] clients)
        {
            List<string> usersToRemove = new List<string>();

            IEnumerable<string> friendClients = cli.Select(g=>g.Key).Intersect(clients.Select(f=>f.username));

            IEnumerable<KeyValuePair<string, IClientReceiverCallback>> cliDic = cli.Where(v => friendClients.Contains(v.Key));

            foreach (KeyValuePair<string, IClientReceiverCallback> item in cliDic)
            {
                try
                {
                    if (item.Key != c2p.username)
                        item.Value.NotifyLoginUser(c2p);
                }
                catch
                {
                    usersToRemove.Add(item.Key);
                }
            }

            if (usersToRemove.Count > 0)
            {
                foreach (string user in usersToRemove)
                {
                    Logout(user);
                }
            }
        }

        private void NotifyUsersLogOut(ClientToPass c2p)
        {
            List<string> usersToRemove = new List<string>();

            IClient[] clients = Tema_User_Mapper.GetInstance().getUsersByTemas(Tema_User_Mapper.GetInstance().
                                           GetTemasForUser(c2p.username), c2p.username).ToArray();

            IEnumerable<string> friendClients = cli.Select(g => g.Key).Intersect(clients.Select(f => f.username));

            IEnumerable<KeyValuePair<string, IClientReceiverCallback>> cliDic = cli.Where(v => friendClients.Contains(v.Key));

            foreach (KeyValuePair<string, IClientReceiverCallback> item in cliDic)
            {
                try
                {
                    if (item.Key != c2p.username)
                        item.Value.NotifyLogoutUser(c2p);
                }
                catch (Exception)
                {
                    usersToRemove.Add(item.Key);

                }
            }

            if (usersToRemove.Count > 0)
            {
                foreach (string user in usersToRemove)
                {
                    Logout(user);
                }
            }
        }
    }
}
